home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / EnhanceMail.1.3 / Source / Send.m < prev    next >
Text File  |  1996-04-08  |  19KB  |  743 lines

  1. /* -*-C-*-
  2. *******************************************************************************
  3. *
  4. * File:         Send.m
  5. * RCS:          $Header: /usr/local/lib/cvs/EnhanceMail/Send.m,v 1.1.1.20 1996/04/08 22:30:51 cedman Exp $
  6. * Description:  
  7. * Author:       Carl Edman
  8. * Created:      Fri Oct 13 11:48:05 1995
  9. * Modified:     Mon Apr  8 11:10:00 1996 (Carl Edman) cedman@capitalist.princeton.edu
  10. * Language:     C
  11. * Package:      N/A
  12. * Status:       Experimental (Do Not Distribute)
  13. *
  14. * (C) Copyright 1995, but otherwise this file is perfect freeware.
  15. *
  16. *******************************************************************************
  17. */
  18.  
  19. #import <ctype.h>
  20. #import "EnhanceMail.h"
  21. #import "Send.h"
  22. #import "SimpleString.h"
  23. #import "Preferences.h"
  24. #import "XFace.h"
  25. #import "regexp.h"
  26.  
  27. static HashTable *SignatureHash=nil;
  28. static HashTable *QuotationHash=nil;
  29. static HashTable *ReplyBoxHash=nil;
  30. static HashTable *InReplyToHash=nil;
  31.  
  32. /* RFC822 Date:
  33.    ^( *(Mon|Tue|Wed|Thu|Fri|Sat|Sun) *, )? *([0-9][0-9]?) +(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) +([0-9][0-9][0-9]?[0-9]?) +([0-9]?[0-9]:[0-9][0-9](:[0-9][0-9])?) +(UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|1[A-Z]|\+[0-9][0-9][0-9][0-9]|-[0-9][0-9][0-9][0-9])
  34.    */
  35.  
  36. static id interpret_escapes(const char *c, id mes, regexp *rx)
  37.    {
  38.    id s=[[SimpleString alloc] init];
  39.    const char *end=c+strlen(c),*d;
  40.    char *m;
  41.    int n;
  42.    static regexp *daterx=0, *addrrx=0;
  43.  
  44.    if (!daterx)
  45.       daterx=regcomp("^(([ \t]*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)[ \t]*,[ \t])?[ \t]*([0-9][0-9]?)[ \t]+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ \t]+([0-9][0-9][0-9]?[0-9]?))[ \t]+([0-9]?[0-9]:[0-9][0-9](:[0-9][0-9])?)");
  46.    if (!addrrx)
  47.       addrrx=regcomp("^[ \t]*(([^ \t].*)?[^ \t])[ \t]+<.*>[ \t]*$");
  48.    
  49.    while(c<end) if ((*c=='%')) switch((++c<end) ? *c++ : '%')
  50.       {
  51.     case '{':
  52.       for(d=c;(c<end) && (*c!='}');c++);
  53.       m=alloca(c-d+1);
  54.       strncpy(m,d,c-d);
  55.       m[c-d]='\0';
  56.       c++;
  57.       if (!(d=[mes headerValueForKey:m])) break;
  58.       [s appendString:d];
  59.       break;
  60.       
  61.     case 'd':
  62.       if ((d=[mes headerValueForKey:"Date"])==0) break;
  63.       if (daterx && regexec(daterx,d))
  64.          [s appendString:daterx->startp[1] length:daterx->endp[1]-daterx->startp[1]];
  65.       break;
  66.       
  67.     case 't':
  68.       if ((d=[mes headerValueForKey:"Date"])==0) break;
  69.       if (daterx && regexec(daterx,d))
  70.          [s appendString:daterx->startp[7] length:daterx->endp[7]-daterx->startp[7]];
  71.       break;
  72.       
  73.     case 'f':
  74.       if (!(d=[mes headerValueForKey:"From"])) break;
  75.       if (addrrx && regexec(addrrx,d)) 
  76.          [s appendString:addrrx->startp[1] length:addrrx->endp[1]-addrrx->startp[1]];
  77.       else
  78.          [s appendString:d];
  79.       break;
  80.  
  81.     case 'n':
  82.       [s appendChar:'\n'];
  83.       break;
  84.       
  85.     case '0':
  86.     case '1':
  87.     case '2':
  88.     case '3':
  89.     case '4':
  90.     case '5':
  91.     case '6':
  92.     case '7':
  93.     case '8':
  94.     case '9':
  95.       for(n=*(c-1)-'0';isdigit(*c) && (c<end);c++) n=n*10+(*c-'0');
  96.       if (n<NSUBEXP && rx && rx->startp[n] && rx->endp[n])
  97.          [s appendString:rx->startp[n] length:rx->endp[n]-rx->startp[n]];
  98.       break;
  99.  
  100.     default:
  101.       [s appendChar:*(c-1)];  
  102.       }
  103.    else
  104.       [s appendChar:*(c++)];
  105.    
  106.    return s;
  107.    }
  108.  
  109. #ifndef KANJI
  110. static id quote_text(const char *text,int length,id mes)
  111.    {
  112.    id s=[[SimpleString alloc] init],prefix=nil;
  113.    int linebeg=0,i;
  114.    int maxlen=[MailMessage lineLength];
  115.    const char *line,*end,*word,*tmp,*prebeg,*preend;
  116.    regexp *rx=regcomp(EnhanceQuoteRegex);
  117.  
  118.    if (!rx) return s;
  119.    
  120.    prebeg=preend=line=text;
  121.    end=line+length;
  122.    
  123.    while(line<end)
  124.       {
  125.       if (!regexec(rx,line)) for(i=0;i<NSUBEXP;i++) rx->startp[i]=rx->endp[i]=line;
  126.       word=rx->endp[0];
  127.       
  128.       if ((prefix==nil) ||
  129.           ((word-line)!=(preend-prebeg)) ||
  130.           (strncmp(line,prebeg,preend-prebeg)!=0))
  131.          {
  132.          prebeg=line;
  133.          preend=word;
  134.          if (prefix!=nil) prefix=[prefix free];
  135.          prefix=interpret_escapes(EnhanceQuotePrefix, mes, rx);
  136.          if (linebeg!=[s length]) linebeg=[s appendChar:'\n'];
  137.          }
  138.       
  139.       while((word<end) && ((*word==' ')||(*word=='\t'))) word++;
  140.  
  141.       for(line=word;(line<end)&&(*line!='\n')&&(*line!='\r');line++);
  142.  
  143.       if ((*word=='\n')||(*word=='\r'))
  144.          {
  145.          if (linebeg!=[s length]) [s appendChar:'\n'];
  146.          [s appendSimpleString:prefix];
  147.          linebeg=[s appendChar:'\n'];
  148.          }
  149.       else if ([s length]-linebeg+(line-word)+
  150.                (([s length]==linebeg) ? [prefix length] : 1)<maxlen)
  151.          {
  152.          if ([s length]==linebeg)
  153.             [s appendSimpleString:prefix];
  154.          else
  155.             [s appendChar:' '];
  156.          [s appendString:word length:(line-word)];
  157.          linebeg=[s appendChar:'\n'];
  158.          }
  159.       else while(word<line)
  160.          {
  161.          for(tmp=word;(tmp<line)&&!((*tmp==' ')||(*tmp=='\t'));tmp++);
  162.          if (!isascii(*tmp)) tmp++;
  163.      
  164.          if ([s length]-linebeg+(tmp-word)+1>=maxlen)
  165.             linebeg=[s appendChar:'\n'];
  166.          
  167.          if ([s length]==linebeg)
  168.             [s appendSimpleString:prefix];
  169.          else
  170.             [s appendChar:' '];
  171.          for(;word<tmp;word++) [s appendChar:*word];
  172.          for(;(word<line)&&((*word==' ')||(*word=='\t'));word++);
  173.          }
  174.       
  175.       if (line<end) line++;
  176.       if ((line<end)&&((*line=='\n')||(*line=='\r'))&&(*line!=*(line-1))) line++;
  177.       }
  178.    
  179.    if (linebeg!=[s length]) [s appendChar:'\n'];
  180.    if (rx) free(rx);
  181.    if (prefix!=nil) [prefix free];
  182.    return s;
  183.    }
  184.  
  185. #else /* KANJI */
  186.  
  187. static int iseow(const char *d)
  188.    {
  189.    static char *oldd;
  190.    static int gotFirstByte=0;
  191.  
  192.    if (d==0)
  193.       {
  194.       gotFirstByte=0;
  195.       return 0;
  196.       }
  197.    
  198.    if (isascii(*d)) 
  199.       {
  200.       if ((*d==' ')||(*d=='\t')) return 1;
  201.       else  return 0;
  202.       }
  203.    else 
  204.       {
  205.       if (gotFirstByte)
  206.          {
  207.          if (oldd != d) gotFirstByte=0;
  208.          oldd = (char *)d; return 1;
  209.          }
  210.       else
  211.          {
  212.           if (oldd != d) gotFirstByte=1;
  213.           oldd = (char *)d; 
  214.       if (isascii(*(d-1))&&(*(d-1)!='\n')&&(*(d-1)!='\r')&&(*(d-1)!=' ')&&(*(d-1)!='\t'))
  215.          return 1;
  216.       else 
  217.          return 0;     
  218.          }
  219.       }
  220.    }
  221.  
  222. static id quote_text(const char *text,int length,id mes)
  223.    {
  224.    id s=[[SimpleString alloc] init],prefix=nil;
  225.    int linebeg=0,i;
  226.    int maxlen=[MailMessage lineLength];
  227.    const char *line,*end,*word,*tmp,*prebeg,*preend;
  228.    regexp *rx=regcomp(EnhanceQuoteRegex);
  229.    char *lasteow;
  230.  
  231.    if (!rx) return s;
  232.    
  233.    prebeg=preend=line=text;
  234.    end=line+length;
  235.    iseow(0); tmp=0;
  236.    
  237.    while(line<end)
  238.       {
  239.       if (!regexec(rx,line)) for(i=0;i<NSUBEXP;i++) rx->startp[i]=rx->endp[i]=line;
  240.       word=rx->endp[0];
  241.       
  242.       if ((prefix==nil) ||
  243.           ((word-line)!=(preend-prebeg)) ||
  244.           (strncmp(line,prebeg,preend-prebeg)!=0))
  245.          {
  246.          prebeg=line;
  247.          preend=word;
  248.          if (prefix!=nil) prefix=[prefix free];
  249.          prefix=interpret_escapes(EnhanceQuotePrefix, mes, rx);
  250.          if (linebeg!=[s length]) linebeg=[s appendChar:'\n'];
  251.          }
  252.       
  253.       while((word<end) && iseow(word)) word++;
  254.  
  255.       for(line=word;(line<end)&&(*line!='\n')&&(*line!='\r');line++);
  256.  
  257.       if ((*word=='\n')||(*word=='\r'))
  258.          {
  259.          if (linebeg!=[s length]) [s appendChar:'\n'];
  260.          [s appendSimpleString:prefix];
  261.          linebeg=[s appendChar:'\n'];
  262.          iseow(0);
  263.          }
  264.       else if ([s length]-linebeg+(line-word)+
  265.                (([s length]==linebeg) ? [prefix length] : 1)<maxlen)
  266.          {
  267.          if ([s length]==linebeg)
  268.             [s appendSimpleString:prefix];
  269.          else
  270.             if (isascii(*(word-2))) [s appendChar:' '];
  271.          [s appendString:word length:(line-word)];
  272.          linebeg=[s appendChar:'\n'];
  273.          iseow(0);
  274.          }
  275.       else {
  276.          while(word<line)
  277.          {
  278.          lasteow=(char *)tmp;
  279.          for(tmp=word;(tmp<line)&&!iseow(tmp);tmp++);
  280.          if (!isascii(*tmp)) tmp++;
  281.      
  282.          if ([s length]-linebeg+(tmp-word)+1>=maxlen)
  283.             {
  284.             if (!isascii(*lasteow)) [s appendChar:*lasteow];
  285.         linebeg=[s appendChar:'\n'];
  286.             }
  287.          
  288.          if ([s length]==linebeg)
  289.             [s appendSimpleString:prefix];
  290.          else
  291.             {
  292.         if (!isascii(*lasteow)&&(*(lasteow+1)!='\n')&&(*(lasteow+1)!='\r')) 
  293.            [s appendString:lasteow length:word-lasteow];
  294.         else if (isascii(*lasteow)) [s appendChar:' '];
  295.             }
  296.          for(;word<tmp;word++) [s appendChar:*word];
  297.          for(;(word<line)&&iseow(word);word++);
  298.          }
  299.          if (!isascii(*tmp)&&(*(tmp+1)=='\n')) [s appendChar:*tmp];
  300.          iseow(0);
  301.          }
  302.       
  303.       if (line<end) line++;
  304.       if ((line<end)&&((*line=='\n')||(*line=='\r'))&&(*line!=*(line-1))) line++;
  305.       }
  306.    
  307.    if (linebeg!=[s length]) [s appendChar:'\n'];
  308.    if (rx) free(rx);
  309.    if (prefix!=nil) [prefix free];
  310.    return s;
  311.    }
  312. #endif /* KANJI */
  313.  
  314. @implementation EnhanceSend
  315. + finishLoading:(struct mach_header *)header
  316.    {
  317.    [self poseAs:[self superclass]];
  318.    SignatureHash=[[HashTable alloc] initKeyDesc:"@" valueDesc:"i"];
  319.    QuotationHash=[[HashTable alloc] initKeyDesc:"@" valueDesc:"i"];
  320.    ReplyBoxHash=[[HashTable alloc] initKeyDesc:"@" valueDesc:"*"];
  321.    InReplyToHash=[[HashTable alloc] initKeyDesc:"@" valueDesc:"*"];
  322.    return self;
  323.    }
  324.  
  325. + startUnloading
  326.    {
  327.    SignatureHash=[SignatureHash free];
  328.    QuotationHash=[QuotationHash free];
  329.    ReplyBoxHash=[ReplyBoxHash free];
  330.    InReplyToHash=[InReplyToHash free];
  331.    return self;
  332.    }
  333.  
  334. - initFrame:(const NXRect *)frameRect
  335.    {
  336.    id ret=[super initFrame:frameRect];
  337.    [SignatureHash insertKey:self value:(void *)(EnhanceInsertSignature^EnhanceControlP())];
  338.    [QuotationHash insertKey:self value:(void *)(NO)];
  339.    [ReplyBoxHash insertKey:self value:0];
  340.    [InReplyToHash insertKey:self value:0];
  341.    return ret;
  342.    }
  343.  
  344. - free
  345.    {
  346.    char *tmp;
  347.    
  348.    [SignatureHash removeKey:self];
  349.    [QuotationHash removeKey:self];
  350.    
  351.    tmp=[ReplyBoxHash valueForKey:self];
  352.    [ReplyBoxHash removeKey:self];
  353.    if (tmp) free(tmp);
  354.    
  355.    tmp=[InReplyToHash valueForKey:self];
  356.    [InReplyToHash removeKey:self];
  357.    if (tmp) free(tmp);
  358.    
  359.    return [super free];
  360.    }
  361.  
  362. - deliver:message
  363.    {
  364.    id ret, def;
  365.    char *replybox=0, *buf=0;
  366.    
  367.    if (EnhanceAutoSpellCheck ^ EnhanceAlternateP())
  368.       {
  369.       id spell=[NXSpellChecker sharedInstance];
  370.       id panel=(spell!=nil) ? [spell spellingPanel] : nil;
  371.  
  372.       if ((spell!=nil)
  373.           &&[spell checkSpelling:NX_CheckSpellingFromStart of:self])
  374.          {
  375.          if (panel!=nil) [panel orderFront:self];
  376.          return nil;
  377.          }
  378.       else
  379.          {
  380.          if (panel!=nil) [panel orderOut:self];
  381.          }
  382.       }
  383.    
  384.    replybox=[ReplyBoxHash valueForKey:self];
  385.    def=[Defaults new];
  386.       
  387.    if (EnhanceRepliesWithOriginals && replybox)
  388.       {
  389.       buf=[def outgoingMailbox];
  390.       buf=strcpy(alloca(strlen(buf)+1),buf);
  391.       [def setOutgoingMailbox:replybox];
  392.       }
  393.    
  394.    ret=[super deliver:message];
  395.  
  396.    if (buf) [def setOutgoingMailbox:buf];
  397.    
  398.    return ret;
  399.    }
  400.  
  401. - mailMessage
  402.    {
  403.    id mes=[super mailMessage];
  404.    char *tmp;
  405.    id ximage;
  406.    char buf[1024];
  407.  
  408.    sprintf(buf,"Mail 3.3 (Enhance %s)",EnhanceVersion);
  409.    [mes setHeaderKey:"X-Nextstep-Mailer" value:buf];
  410.  
  411.    if (tmp=[InReplyToHash valueForKey:self])
  412.       [mes setHeaderKey:"In-Reply-To" value:tmp];
  413.    
  414.    if (EnhanceInsertXFace && EnhanceXFace
  415.        && (ximage=[[NXImage alloc] initFromFile:EnhanceXFace]))
  416.       {
  417.       tmp=[ximage xFace];
  418.       ximage=[ximage free];
  419.       if (tmp)
  420.          {
  421.          [mes setHeaderKey:"X-Face" value:tmp];
  422.          free(tmp);
  423.          }
  424.       }
  425.  
  426.    if (EnhanceInsertXImageURL && EnhanceXImageURL)
  427.       {
  428.       tmp=malloc(strlen(EnhanceXImageURL)+1);
  429.       strcpy(tmp,EnhanceXImageURL);
  430.       [mes setHeaderKey:"X-Image-URL" value:tmp];
  431.       free(tmp);
  432.       }
  433.    
  434.    return mes;
  435.    }
  436.  
  437. - (void)defaultsChanged
  438.    {
  439.    [super defaultsChanged];
  440.    if (!editing)
  441.       [self initQuoteSig];
  442.    }
  443.  
  444. - (void)setSendFormat:(int)arg
  445.    {
  446.    [super setSendFormat:arg];
  447.    if (!editing)
  448.       [self initQuoteSig];
  449.    }
  450.  
  451. - replyAll:sender
  452.    {
  453.    id ret=[super replyAll:sender];
  454.    [self prepareReply];
  455.    return ret;
  456.    }
  457.  
  458. - reply:sender
  459.    {
  460.    id ret=[super reply:sender];
  461.    [self prepareReply];
  462.    return ret;
  463.    }
  464.  
  465. - restoreDraftFromMessage:message
  466.    {
  467. #if 0
  468.    if (EnhanceRestoreDeletesDraft)
  469.       {
  470.       [toc setState:'d' forMsg:number flush:YES];
  471.       }
  472. #endif
  473.    [self nowEditing];
  474.    [text setText:""];
  475.    [self updateSpecialDelivery];
  476.    return [super restoreDraftFromMessage:message];
  477.    }
  478.  
  479. - prepareReply
  480.    {
  481.    id oreader=[self getMailbox];
  482.    id obox=oreader ? [oreader mailbox] : nil;
  483.    id orig=oreader ? [oreader mailMessage] : nil;
  484.    char *old,*new;
  485.    
  486.    [QuotationHash insertKey:self value:(void *)(EnhanceQuoteReplies^EnhanceControlP())];
  487.  
  488.    old=[ReplyBoxHash valueForKey:self];
  489.    new=obox ? [obox dirname] : 0;
  490.    if ((old && new && strcmp(old,new))
  491.        || (old==0 && new!=0)
  492.        || (new!=0 && old==0))
  493.       {
  494.       if (old) free(old);
  495.       if (new) new=strcpy(malloc(strlen(new)+1),new);
  496.       [ReplyBoxHash insertKey:self value:new];
  497.       }
  498.    
  499.    old=[InReplyToHash valueForKey:self];
  500.    new=orig ? [orig headerValueForKey:"Message-Id"] : 0;
  501.    if ((old && new && strcmp(old,new))
  502.        || (old==0 && new!=0)
  503.        || (new!=0 && old==0))
  504.       {
  505.       if (old) free(old);
  506.       if (new) new=strcpy(malloc(strlen(new)+1),new);
  507.       [InReplyToHash insertKey:self value:new];
  508.       }
  509.  
  510.    if ((orig!=nil)&&EnhanceFlagReplies)
  511.       [oreader setFlagged:YES];
  512.    
  513. #ifdef KANJI
  514.    [self logNote:"The value of editing (%d) is ignored on NS3.3J.",editing];
  515.    [self initQuoteSig];
  516. #else /* not KANJI */
  517.    if (!editing)
  518.       [self initQuoteSig];
  519. #endif /* kANJI */
  520.       
  521.    return self;
  522.    }
  523.  
  524. - initQuoteSig
  525.    {
  526.    id oreader=[self getMailbox];
  527.    id obox=oreader ? [oreader mailbox] : nil;
  528.    id orig=oreader ? [oreader mailMessage] : nil;
  529.    id otext=oreader ? [oreader text] : nil;
  530.    BOOL quotationflag=(int)[QuotationHash valueForKey:self];
  531.    BOOL signatureflag=(int)[SignatureHash valueForKey:self];
  532.  
  533.    [text setAutodisplay:NO];
  534.    [text setText:""];
  535.    
  536.    if (signatureflag)
  537.       {
  538.       char path[FILENAME_MAX];
  539.       const char *home=NXHomeDirectory();
  540.       const char *box=obox ? [obox dirname] : 0;
  541.       const char *file=EnhanceSignatureFilename;
  542.       BOOL success=NO;
  543.  
  544.       if (file && box && !success)
  545.          {
  546.          sprintf(path,"%s/%s.rtfd",box,file);
  547.          if (sendFormat!=0 && ([text openRTFDFrom:path]==NX_RTFDErrorNone))
  548.             success=YES;
  549.  
  550.          sprintf(path,"%s/%s.rtf",box,file);
  551.          if (!success && sendFormat!=0 && ([text openRichFrom:path]!=nil))
  552.             success=YES;
  553.  
  554.          sprintf(path,"%s/%s",box,file);
  555.          if (!success && ([text openFrom:path]!=nil))
  556.             success=YES;
  557.          }
  558.  
  559.       if (file && home && !success)
  560.          {
  561.          sprintf(path,"%s/%s.rtfd",home,file);
  562.          if (sendFormat!=0 && ([text openRTFDFrom:path]==NX_RTFDErrorNone))
  563.             success=YES;
  564.  
  565.          sprintf(path,"%s/%s.rtf",home,file);
  566.          if (!success && sendFormat!=0 && ([text openRichFrom:path]!=nil))
  567.             success=YES;
  568.  
  569.          sprintf(path,"%s/%s",home,file);
  570.          if (!success && ([text openFrom:path]!=nil))
  571.             success=YES;
  572.          }
  573.       
  574.       if (success)
  575.          {
  576.          [text setSel:0:0];
  577.          [text replaceSel:EnhanceSignatureSeparator];
  578.          }
  579.       }
  580.  
  581.    if (quotationflag && orig)
  582.       {
  583.       id s=[[SimpleString alloc] init];
  584.  
  585.       if (EnhanceQuoteIntro)
  586.          {
  587.          [s includeSimpleString:interpret_escapes(EnhanceQuoteIntro,orig,0)];
  588.          [s appendChar:'\n'];
  589.          }
  590.  
  591.       if (otext)
  592.          {
  593.          char *buf,*c;
  594.          int start,len;
  595.          NXSelPt beg,end;
  596.          
  597.          [otext getSel:&beg:&end];
  598.          if (beg.cp!=end.cp)
  599.             {
  600.             start=beg.cp;
  601.             len=end.cp-beg.cp;
  602.             buf=alloca(len);
  603.             [otext getSubstring:buf start:start length:len];
  604.             }
  605.          else
  606.             {
  607.             start=0;
  608.             len=[otext byteLength];
  609.             buf=alloca(len);
  610.             [otext getSubstring:buf start:start length:len];
  611.             for(c=buf;c<buf+len-1;c++) if ((c[0]=='\n') && (c[1]=='\n')) break;
  612.             while ((c<buf+len) && (*c=='\n')) c++;
  613.             len-=(c-buf);
  614.             buf=c;
  615.             }
  616.  
  617.          start=[s length];
  618.          [s includeSimpleString:quote_text(buf,len,orig)];
  619.          len=[s length]-start;
  620.          
  621.          [text setSel:0:0];
  622.          [text replaceSel:[s string] length:[s length]];
  623.  
  624.          if ((sendFormat!=0)&&EnhanceQuoteColoring)
  625.             {
  626.             [text setSel:start:start+len];
  627.             [text setSelColor:EnhanceQuoteColor];
  628.             }
  629.          
  630.          [text setSel:[s length]:[s length]];
  631.          }
  632.       
  633.       s=[s free];
  634.       }
  635.    [text setSel:0:0];
  636.    
  637.    if (sendFormat==0) [self makeAscii:self];
  638.    [self updateSpecialDelivery];
  639.    
  640.    [text setAutodisplay:YES];
  641.    [text display];
  642.    return self;
  643.    }
  644.  
  645. - openTextStream
  646.    {
  647.    [text openTextStream];
  648.    return self;
  649.    }
  650.  
  651. - (BOOL)seekToCharacterAt:(int)offset relativeTo:(int)seekMode
  652.    {
  653.    return [text seekToCharacterAt:offset relativeTo:seekMode];
  654.    }
  655.  
  656. - (int)readCharacters:(char *)buffer count:(int)count
  657.    {
  658.    return [text readCharacters:buffer count:count];
  659.    }
  660.  
  661. - (int)currentCharacterOffset
  662.    {
  663.    return [text currentCharacterOffset];
  664.    }
  665.  
  666. - (BOOL)isAtEOTS
  667.    {
  668.    return [text isAtEOTS];
  669.    }
  670.  
  671. - closeTextStream
  672.    {
  673.    [text closeTextStream];
  674.    return self;
  675.    }
  676.  
  677. - (void)selectCharactersFrom:(int)start to:(int)end
  678.    {
  679.    [text selectCharactersFrom:start to:end];
  680.    }
  681.  
  682. - (int)selectionCharacterCount
  683.    {
  684.    return [text selectionCharacterCount];
  685.    }
  686.  
  687. - (int)readCharactersFromSelection:(char *)buffer count:(int)count
  688.    {
  689.    return [text readCharactersFromSelection:buffer count:count];
  690.    }
  691.  
  692. - (void)makeSelectionVisible
  693.    {
  694.    [text makeSelectionVisible];
  695.    }
  696.  
  697. - changeSpelling:sender
  698.    {
  699.    return ([text changeSpelling:sender]==nil) ? nil : self;
  700.    }
  701.  
  702. - (int)spellDocumentTag
  703.    {
  704.    return [text spellDocumentTag];
  705.    }
  706. @end
  707.  
  708. @implementation MediaText(EnhanceSend)
  709. - openFrom:(const char *)file
  710.    {
  711.    BOOL ok=YES;
  712.    NXStream *s=0;
  713.    
  714.    if (ok && !(s=NXMapFile(file,NX_READONLY)))
  715.       ok=NO;
  716.    
  717.    if (ok && ([self readText:s]==nil))
  718.       ok=NO;
  719.    
  720.    if (s)
  721.       NXClose(s);
  722.    
  723.    return ok ? self : nil;
  724.    }
  725.  
  726. - openRichFrom:(const char *)file
  727.    {
  728.    BOOL ok=YES;
  729.    NXStream *s=0;
  730.    
  731.    if (ok && !(s=NXMapFile(file,NX_READONLY)))
  732.       ok=NO;
  733.    
  734.    if (ok && ([self readRichText:s]==nil))
  735.       ok=NO;
  736.    
  737.    if (s)
  738.       NXClose(s);
  739.    
  740.    return ok ? self : nil;
  741.    }
  742. @end
  743.